/**

This file is part of MaCI/GIMnet.

MaCI/GIMnet is free software: you can redistribute it and/or modify it 
under the terms of the GNU Lesser General Public License as published 
by the Free Software Foundation, either version 3 of the License, or 
(at your option) any later version.

MaCI/GIMnet is distributed in the hope that it will be useful, but WITHOUT 
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 
FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public 
License for more details.

You should have received a copy of the GNU Lesser General Public 
License along with GIMnet. (See COPYING.LESSER) If not, see 
<http://www.gnu.org/licenses/>.

**/
/**
 * \file
 * \brief SVG Image data decoder/encoder - implementation.
 * \author Antti Maula <antti.maula@tkk.fi>
 */

#include "SVGImage.hpp"
#include "owndebug.h"
#include <assert.h>

//*****************************************************************************

CSVGImage::CSVGImage()
  : iXMLData(),
    iObjectArray()
{
}
//*****************************************************************************

CSVGImage::~CSVGImage()
{
}
//*****************************************************************************

const TSVGObjectArray &CSVGImage::GetObjectArray()
{
  return iObjectArray;
}
//*****************************************************************************

bool CSVGImage::DecodeSVGString(const std::string &aSVG)
{
  xmlDocPtr docp;
  xmlNodePtr nodep;
  bool result = false;

  docp = xmlRecoverDoc((xmlChar *) aSVG.c_str());
  if (docp == NULL) {
    dPrint(ODWARN,"Failed to decode XML doc. Fatal error. Decoding aborted.");
    
  } else {
  
    // Get root element
    nodep = xmlDocGetRootElement(docp);
    if (nodep) {
      // Found. Call Decode for the data.
      result = DecodeSVG(docp, nodep);
      
    }

    xmlFreeDoc(docp);
  }

  return result;
}
//*****************************************************************************

bool CSVGImage::DecodeSVGFile(const std::string &aSVG)
{ 
  xmlDocPtr docp;
  xmlNodePtr nodep;
  bool result = false;

  docp = xmlRecoverFile((const char *) aSVG.c_str());
  if (docp == NULL) {
    dPrint(ODWARN,"Failed to decode XML doc. Fatal error. Decoding aborted.");
    
  } else {
  
    // Get root element
    nodep = xmlDocGetRootElement(docp);
    if (nodep) {
      // Found. Call Decode for the data.
      result = DecodeSVG(docp, nodep);
      
    } else {
      dPrint(ODERROR,"No Root element found for XML-file '%s'! Fatal error. Abort.", aSVG.c_str());

    }

    xmlFreeDoc(docp);
  }

  return result;
}
//*****************************************************************************

bool CSVGImage::DecodeSVG(xmlDocPtr docp, xmlNodePtr nodep)
{
  bool result = false;
  assert(docp != NULL);
  assert(nodep != NULL);

  // Check that we have a SVG
  if( xmlStrcmp(nodep->name, (xmlChar *) "svg")) {
    dPrint(ODWARN, "This is not a SVG XML document (Invalid root node: '%s')",
           nodep->name);
    
  } else {
    
    // Check SVG root-tag attributes
    xmlChar *svgwidth = xmlGetProp(nodep, (xmlChar *) "width");
    xmlChar *svgheight = xmlGetProp(nodep, (xmlChar *) "height");
    xmlChar *svgviewBox = xmlGetProp(nodep, (xmlChar *) "viewBox");
    if (svgwidth) {
      //            dPrint(ODTEST,"SVG width is '%s'", svgwidth);
      xmlFree(svgwidth);
    }
    if (svgheight) {
      //            dPrint(ODTEST,"SVG height is '%s'", svgheight);
      xmlFree(svgheight);
    }
    if (svgviewBox) {
      //            dPrint(ODTEST,"SVG viewBox is '%s'", svgviewBox);
      xmlFree(svgviewBox);
    }

    // Enter children level of root.
    nodep = nodep->children;
    
    // parse 
    result = true;
    while(nodep && result) {
      if (nodep->type == XML_ELEMENT_NODE) {
        if ( !xmlStrcmp(nodep->name, (xmlChar *) "line") ) {
          result = DecodeTagLine(docp, nodep);
          
        } else if ( !xmlStrcmp(nodep->name, (xmlChar *) "rect") ) {
          result = DecodeTagRect(docp, nodep);
          
        } else if ( !xmlStrcmp(nodep->name, (xmlChar *) "ellipse") ) {
          result = DecodeTagEllipse(docp, nodep);
          
        } else {
          dPrint(ODERROR,"Unknown tag '%s' in level 0", 
                 nodep->name);
          
        }
      }
      
      // Traverse to next node in level.
      nodep = nodep->next;
    }
  }
  
  // Return result.
  return result;
}
//*****************************************************************************

bool CSVGImage::DecodeTagLine(xmlDocPtr docp, xmlNodePtr nodep)
{
  bool result = false;
  SSVGStyle style;
  float x1=0,y1=0,x2=0,y2=0;

  //  dPrint(ODTEST,"Decoding Line");

  // Parameters for a Line.
  xmlChar *svgstyle = xmlGetProp(nodep, (xmlChar *) "style");
  xmlChar *svgx1 = xmlGetProp(nodep, (xmlChar *) "x1");
  xmlChar *svgy1 = xmlGetProp(nodep, (xmlChar *) "y1");
  xmlChar *svgx2 = xmlGetProp(nodep, (xmlChar *) "x2");
  xmlChar *svgy2 = xmlGetProp(nodep, (xmlChar *) "y2");

  // Process.
  if (svgstyle && svgx1 && svgy1 && svgx2 && svgy2) {
    result = style.DecodeStyle(std::string((const char *)svgstyle));
    x1 = atof((const char*)svgx1);
    y1 = atof((const char*)svgy1);
    x2 = atof((const char*)svgx2);
    y2 = atof((const char*)svgy2);
  }

  // Cleanup (Always do this to clean out properties which were found)
  if (svgstyle) xmlFree(svgstyle);
  if (svgx1) xmlFree(svgx1);
  if (svgy1) xmlFree(svgy1);
  if (svgx2) xmlFree(svgx2);
  if (svgy2) xmlFree(svgy2);


  // Now, after done; proceed adding object
  if (result) {
    //    dPrint(ODVINFO,"Line decoded: x1:%f, y1:%f, x2:%f, y2:%f", x1,y1,x2,y2);
    SSVGObject obj;
    obj.InitLine(style, x1,y1, x2,y2);
    AddObject(obj);
  } else {
    dPrint(ODWARN,"Line decoding failed!");
  
  }
  
  return result;
}
//*****************************************************************************

bool CSVGImage::DecodeTagRect(xmlDocPtr docp, xmlNodePtr nodep)
{
  //  <rect style="fill: none; fill-opacity:0; stroke-width: 0.001; stroke: #ff0000" x="-27.3673" y="-19.2985" 
  //   width="0.736" height="0.656" rx="1.17549e-38" ry="1.17549e-38"/>
  bool result = false;

  //  dPrint(ODTEST,"Decoding Rect");
  
  SSVGStyle style;
  float x=0,y=0,width=0,height=0,rx=0,ry=0;
  
  // Parameters for a Line.
  xmlChar *svgstyle = xmlGetProp(nodep, (xmlChar *) "style");
  xmlChar *svgx = xmlGetProp(nodep, (xmlChar *) "x");
  xmlChar *svgy = xmlGetProp(nodep, (xmlChar *) "y");
  xmlChar *svgwidth = xmlGetProp(nodep, (xmlChar *) "width");
  xmlChar *svgheight = xmlGetProp(nodep, (xmlChar *) "height");
  xmlChar *svgrx = xmlGetProp(nodep, (xmlChar *) "rx");
  xmlChar *svgry = xmlGetProp(nodep, (xmlChar *) "ry");


  // Process.
  if (svgstyle && svgx && svgy && svgwidth && svgheight && svgrx && svgry) {
    result = style.DecodeStyle(std::string((const char *)svgstyle));
    x = atof((const char*)svgx);
    y = atof((const char*)svgy);
    width = atof((const char*)svgwidth);
    height = atof((const char*)svgheight);
    rx = atof((const char*)svgrx);
    ry = atof((const char*)svgry);
    
  }

  // Cleanup (Always do this to clean out properties which were found)
  if (svgstyle) xmlFree(svgstyle);
  if (svgx) xmlFree(svgx);
  if (svgy) xmlFree(svgy);
  if (svgwidth) xmlFree(svgwidth);
  if (svgheight) xmlFree(svgheight);
  if (svgrx) xmlFree(svgrx);
  if (svgry) xmlFree(svgry);
  
  // Now, after done; proceed adding object
  if (result) {
    //    dPrint(ODVINFO,"Rect decoded: x:%f, y:%f, w:%f, h:%f, rx:%f, ry:%f", x,y,width,height,rx,ry);
    SSVGObject obj;
    obj.InitRect(style, x,y,width,height,rx,ry);
    AddObject(obj);

  } else {
    dPrint(ODWARN,"Rect decoding failed!");

  }

  return result;
}
//*****************************************************************************

bool CSVGImage::DecodeTagEllipse(xmlDocPtr docp, xmlNodePtr nodep)
{
  // <ellipse style="fill: none; fill-opacity:0; stroke-width: 0.001; stroke: #000000" 
  // cx="-17.5137" cy="-19.4753" rx="0.2875" ry="0.2875"/>
  bool result = false;

    dPrint(ODTEST,"Decoding Ellipse");
  
  SSVGStyle style;
  float cx=0.0,cy=0.0,rx=0.0,ry=0.0;
  
  // Parameters for a Line.
  xmlChar *svgstyle = xmlGetProp(nodep, (xmlChar *) "style");
  xmlChar *svgcx = xmlGetProp(nodep, (xmlChar *) "cx");
  xmlChar *svgcy = xmlGetProp(nodep, (xmlChar *) "cy");
  xmlChar *svgrx = xmlGetProp(nodep, (xmlChar *) "rx");
  xmlChar *svgry = xmlGetProp(nodep, (xmlChar *) "ry");


  // Process.
  if (svgstyle && svgcx && svgcy && svgrx && svgry) {
    result = style.DecodeStyle(std::string((const char *)svgstyle));
    cx = atof((const char*)svgcx);
    cy = atof((const char*)svgcy);
    rx = atof((const char*)svgrx);
    ry = atof((const char*)svgry);
  }

  // Cleanup (Always do this to clean out properties which were found)
  if (svgstyle) xmlFree(svgstyle);
  if (svgcx) xmlFree(svgcx);
  if (svgcy) xmlFree(svgcy);
  if (svgrx) xmlFree(svgrx);
  if (svgry) xmlFree(svgry);
    
  // Now, after done; proceed adding object
  if (result) {
    SSVGObject obj;
    //dPrint(ODVINFO,"Decoded Ellipse cx:%f, cy:%f, rx:%f, ry:%f", cx, cy, rx, ry);
    obj.InitEllipse(style, cx, cy, rx, ry);
    AddObject(obj);
  } else {
    dPrint(ODWARN,"Ellipse decoding failed!");

  }

  return result;
}
//*****************************************************************************

void CSVGImage::AddObject(SSVGObject &aObject)
{
  iObjectArray.push_back(aObject);
}
//*****************************************************************************
//*****************************************************************************

